/*
 *
 *  Instantatiate a new terminal shell.
 *
 *  Author:
 *
 *   WORK: fernando.ruiz@ctv.es
 *   HOME: correo@fernando-ruiz.com
 *
 *   Thanks at:
 *    Chris John
 *
 *  $Id: shell.c,v 1.7.2.1 2003/02/14 20:09:11 joel Exp $
 */
#include <stdio.h>
#include <time.h>

#include "shell.h"

#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>

/* **************************************************************
 * common linked list of shell commands.
 * Because the help report is very long
 * I have a topic for each command.
 * Help list the topics
 * help [topic] list the commands for the topic
 * help [command] help for the command
 * Can you see help rtems monitor report?
 * ************************************************************** */

struct shell_topic_tt;
typedef struct shell_topic_tt shell_topic_t;

struct shell_topic_tt {
    char * topic;
    shell_topic_t * next;
};


static shell_cmd_t   * shell_first_cmd;
static shell_topic_t * shell_first_topic;
/* ----------------------------------------------- *
 * Using Chain I can reuse the rtems code.
 * I am more comfortable with this, sorry.
 * ----------------------------------------------- */
shell_topic_t * shell_lookup_topic(char * topic) {
  shell_topic_t * shell_topic;
  shell_topic=shell_first_topic;
  while (shell_topic) {
   if (!strcmp(shell_topic->topic,topic)) return shell_topic;
   shell_topic=shell_topic->next;
  };
  return (shell_topic_t *) NULL;
}
/* ----------------------------------------------- */
shell_topic_t * shell_add_topic(char * topic) {
 shell_topic_t * current,*aux;
 if (!shell_first_topic) {
  aux=(void*)malloc(sizeof(shell_topic_t));
  aux->topic=topic;
  aux->next=(shell_topic_t*)NULL;
  return shell_first_topic=aux;
 } else {
  current=shell_first_topic;
  if (!strcmp(topic,current->topic)) return current;
  while (current->next) {
   if (!strcmp(topic,current->next->topic)) return current->next;
   current=current->next;
  };
  aux=(void*)malloc(sizeof(shell_topic_t));
  aux->topic=topic;
  aux->next=(shell_topic_t*)NULL;
  current->next=aux;
  return aux;
 };
}
/* ----------------------------------------------- */
shell_cmd_t * shell_lookup_cmd(char * cmd) {
    shell_cmd_t * shell_cmd;
    shell_cmd=shell_first_cmd;

    while (shell_cmd) {
        if (!strcmp(shell_cmd->name,cmd)) return shell_cmd;
        shell_cmd=shell_cmd->next;
    };

    return (shell_cmd_t *) NULL;
}
/* ----------------------------------------------- */
shell_cmd_t * shell_add_cmd(char * cmd,
              char * topic,
              char * usage,
                      shell_command_t command) {
  int shell_help(int argc,char * argv[]);
  shell_cmd_t * shell_cmd,*shell_pvt;
  if (!shell_first_cmd) {
   shell_first_cmd=(shell_cmd_t *) malloc(sizeof(shell_cmd_t));
   shell_first_cmd->name   ="help";
   shell_first_cmd->topic  ="help";
   shell_first_cmd->usage  ="help [topic] # list of usage of commands";
   shell_first_cmd->command=shell_help;
   shell_first_cmd->alias  =(shell_cmd_t *) NULL;
   shell_first_cmd->next   =(shell_cmd_t *) NULL;
   shell_add_topic(shell_first_cmd->topic);
   register_cmds();
  };
  if (!cmd)     return (shell_cmd_t *) NULL;
  if (!command) return (shell_cmd_t *) NULL;
  shell_cmd=(shell_cmd_t *) malloc(sizeof(shell_cmd_t));
  shell_cmd->name   =cmd;
  shell_cmd->topic  =topic;
  shell_cmd->usage  =usage;
  shell_cmd->command=command;
  shell_cmd->alias  =(shell_cmd_t *) NULL;
  shell_cmd->next   =(shell_cmd_t *) NULL;
  shell_add_topic(shell_cmd->topic);
  shell_pvt=shell_first_cmd;
  while (shell_pvt->next) shell_pvt=shell_pvt->next;
  return shell_pvt->next=shell_cmd;
}
/* ----------------------------------------------- *
 * you can make an alias for every command.
 * ----------------------------------------------- */
shell_cmd_t * shell_alias_cmd(char * cmd, char * alias) {
  shell_cmd_t * shell_cmd,* shell_aux;
  shell_aux=(shell_cmd_t *) NULL;
  if (alias) {
   if ((shell_aux=shell_lookup_cmd(alias))!=NULL) {
    return NULL;
   };
   if ((shell_cmd=shell_lookup_cmd(cmd))!=NULL) {
    shell_aux=shell_add_cmd(alias,shell_cmd->topic,
                    shell_cmd->usage,shell_cmd->command);
    if (shell_aux) shell_aux->alias=shell_cmd;
   };
  };
  return shell_aux;
}
/* ----------------------------------------------- *
 * Poor but enough..
 * TODO: Redirection capture. "" evaluate, ... C&S welcome.
 * ----------------------------------------------- */
int shell_make_args(char * cmd,
         int  * pargc,
         char * argv[]) {
  int argc=0;
  while ((cmd=strtok(cmd," \t\r\n"))!=NULL) {
    argv[argc++]=cmd;
    cmd=(char*)NULL;
   };
  argv[argc]=(char*)NULL;
  return *pargc=argc;
}
/* ----------------------------------------------- *
 * show the help for one command.
 * ----------------------------------------------- */
int shell_help_cmd(shell_cmd_t * shell_cmd) {
  char * pc;
  int    col,line;
  printf("%-18.18s -",shell_cmd->name);
  col=12;
  line=1;
  if (shell_cmd->alias) {
   printf("is an <alias> for command '%s'",shell_cmd->alias->name);
  } else
  if (shell_cmd->usage) {
   pc=shell_cmd->usage;
   while (*pc) {
    switch(*pc) {
     case '\r':break;
     case '\n':putchar('\n');
               col=0;
               break;
     default  :putchar(*pc);
               col++;
               break;
    };
    pc++;
    if(col>78) { /* What daring... 78?*/
     if (*pc) {
      putchar('\n');
      col=0;
     };
    };
    if (!col && *pc) {
      printf("            ");
      col=12;line++;
    };
   };
  };
  puts("");
  return line;
}
/* ----------------------------------------------- *
 * show the help. The first command implemented.
 * Can you see the header of routine? Known?
 * The same with all the commands....
 * ----------------------------------------------- */
int shell_help(int argc,char * argv[]) {
  int col,line,arg;
  shell_topic_t *topic;
  shell_cmd_t * shell_cmd=shell_first_cmd;
  if (argc<2) {
   printf("help:\n"
          "  TOPIC? The topics are");
   topic=shell_first_topic;
   col=0;
   while (topic) {
    if (!col){
     col=printf("   %s",topic->topic);
    } else {
     if ((col+strlen(topic->topic)+2)>78){
      printf("\n");
      col=printf("   %s",topic->topic);
     } else {
      col+=printf(", %s",topic->topic);
     };
    };
    topic=topic->next;
   };
   printf("\n");
   return 1;
  };
  //list all topic name
  if (!strcmp("list",argv[1]))
  {
      
      shell_cmd_t * shell_cmd;
      shell_cmd=shell_first_cmd;
      while (shell_cmd) 
      {
          printf("%s\n",shell_cmd->name);
          shell_cmd=shell_cmd->next;
      };
      return 1;
  }
  
  line=0;
  for (arg=1;arg<argc;arg++) {
   if (line>16) {
    printf("Press any key to continue...");getchar();
    printf("\n");
    line=0;
   };
   topic=shell_lookup_topic(argv[arg]);
   if (!topic){
    if ((shell_cmd=shell_lookup_cmd(argv[arg]))==NULL) {
     printf("help: topic or cmd '%s' not found. Try <help> alone for a list\n",argv[arg]);
     line++;
    } else {
     line+=shell_help_cmd(shell_cmd);
    }
    continue;
   };
   printf("help: list for the topic '%s'\n",argv[arg]);
   line++;
   while (shell_cmd) {
    if (!strcmp(topic->topic,shell_cmd->topic))
     line+=shell_help_cmd(shell_cmd);
    //if (line>16) {
    // printf("Press any key to continue...");getchar();
    // printf("\n");
    // line=0;
    //};
    shell_cmd=shell_cmd->next;
   };
  };
  puts("");
  return 0;
}

